{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.0.0-beta1\n",
      "sys.version_info(major=3, minor=7, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.0.3\n",
      "numpy 1.16.2\n",
      "pandas 0.24.2\n",
      "sklearn 0.20.3\n",
      "tensorflow 2.0.0-beta1\n",
      "tensorflow.python.keras.api._v2.keras 2.2.4-tf\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "import tensorflow as tf\n",
    "\n",
    "from tensorflow import keras\n",
    "\n",
    "print(tf.__version__)\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, tf, keras:\n",
    "    print(module.__name__, module.__version__)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(5000, 28, 28) (5000,)\n",
      "(55000, 28, 28) (55000,)\n",
      "(10000, 28, 28) (10000,)\n"
     ]
    }
   ],
   "source": [
    "fashion_mnist = keras.datasets.fashion_mnist\n",
    "(x_train_all, y_train_all), (x_test, y_test) = fashion_mnist.load_data()\n",
    "x_valid, x_train = x_train_all[:5000], x_train_all[5000:]\n",
    "y_valid, y_train = y_train_all[:5000], y_train_all[5000:]\n",
    "\n",
    "print(x_valid.shape, y_valid.shape)\n",
    "print(x_train.shape, y_train.shape)\n",
    "print(x_test.shape, y_test.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# x = (x - u) / std\n",
    "\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "\n",
    "scaler = StandardScaler()\n",
    "# x_train: [None, 28, 28] -> [None, 784]\n",
    "x_train_scaled = scaler.fit_transform(\n",
    "    x_train.astype(np.float32).reshape(-1, 1)).reshape(-1, 28, 28)\n",
    "x_valid_scaled = scaler.transform(\n",
    "    x_valid.astype(np.float32).reshape(-1, 1)).reshape(-1, 28, 28)\n",
    "x_test_scaled = scaler.transform(\n",
    "    x_test.astype(np.float32).reshape(-1, 1)).reshape(-1, 28, 28)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tf.keras.models.Sequential()\n",
    "\n",
    "\"\"\"\n",
    "model = keras.models.Sequential()\n",
    "model.add(keras.layers.Flatten(input_shape=[28, 28]))\n",
    "model.add(keras.layers.Dense(300, activation=\"relu\"))\n",
    "model.add(keras.layers.Dense(100, activation=\"relu\"))\n",
    "model.add(keras.layers.Dense(10, activation=\"softmax\"))\n",
    "\"\"\"\n",
    "\n",
    "model = keras.models.Sequential([\n",
    "    keras.layers.Flatten(input_shape=[28, 28]),\n",
    "    keras.layers.Dense(300, activation='relu'),\n",
    "    keras.layers.Dense(100, activation='relu'),\n",
    "    keras.layers.Dense(10, activation='softmax')\n",
    "])\n",
    "\n",
    "# relu: y = max(0, x)\n",
    "# softmax: 将向量变成概率分布. x = [x1, x2, x3], \n",
    "#          y = [e^x1/sum, e^x2/sum, e^x3/sum], sum = e^x1 + e^x2 + e^x3\n",
    "\n",
    "# reason for sparse: y->index. y->one_hot->[] \n",
    "model.compile(loss=\"sparse_categorical_crossentropy\",\n",
    "              optimizer = \"sgd\",\n",
    "              metrics = [\"accuracy\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING: Logging before flag parsing goes to stderr.\n",
      "W0716 22:22:02.177092 140736297124800 deprecation.py:323] From /Users/zhangyx/workspace/environments/tf2_py3/lib/python3.7/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support.<locals>.wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Use tf.where in 2.0, which has the same broadcast rule as np.where\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 55000 samples, validate on 5000 samples\n",
      "Epoch 1/10\n",
      "55000/55000 [==============================] - 8s 137us/sample - loss: 0.5390 - accuracy: 0.8114 - val_loss: 0.4112 - val_accuracy: 0.8564\n",
      "Epoch 2/10\n",
      "55000/55000 [==============================] - 7s 126us/sample - loss: 0.3927 - accuracy: 0.8586 - val_loss: 0.3663 - val_accuracy: 0.8722\n",
      "Epoch 3/10\n",
      "55000/55000 [==============================] - 7s 126us/sample - loss: 0.3535 - accuracy: 0.8721 - val_loss: 0.3502 - val_accuracy: 0.8738\n",
      "Epoch 4/10\n",
      "55000/55000 [==============================] - 7s 128us/sample - loss: 0.3291 - accuracy: 0.8812 - val_loss: 0.3357 - val_accuracy: 0.8776\n",
      "Epoch 5/10\n",
      "55000/55000 [==============================] - 7s 124us/sample - loss: 0.3091 - accuracy: 0.8883 - val_loss: 0.3352 - val_accuracy: 0.8802\n",
      "Epoch 6/10\n",
      "55000/55000 [==============================] - 7s 125us/sample - loss: 0.2926 - accuracy: 0.8942 - val_loss: 0.3116 - val_accuracy: 0.8884\n",
      "Epoch 7/10\n",
      "55000/55000 [==============================] - 7s 129us/sample - loss: 0.2791 - accuracy: 0.8989 - val_loss: 0.3210 - val_accuracy: 0.8844\n",
      "Epoch 8/10\n",
      "55000/55000 [==============================] - 7s 123us/sample - loss: 0.2676 - accuracy: 0.9028 - val_loss: 0.3144 - val_accuracy: 0.8846\n",
      "Epoch 9/10\n",
      "55000/55000 [==============================] - 7s 130us/sample - loss: 0.2560 - accuracy: 0.9075 - val_loss: 0.3255 - val_accuracy: 0.8816\n",
      "Epoch 10/10\n",
      "55000/55000 [==============================] - 7s 125us/sample - loss: 0.2462 - accuracy: 0.9118 - val_loss: 0.2978 - val_accuracy: 0.8906\n"
     ]
    }
   ],
   "source": [
    "# Tensorboard, earlystopping, ModelCheckpoint\n",
    "logdir = './graph_def_and_weights'\n",
    "if not os.path.exists(logdir):\n",
    "    os.mkdir(logdir)\n",
    "output_model_file = os.path.join(logdir,\n",
    "                                 \"fashion_mnist_weights.h5\")\n",
    "\n",
    "callbacks = [\n",
    "    keras.callbacks.TensorBoard(logdir),\n",
    "    keras.callbacks.ModelCheckpoint(output_model_file,\n",
    "                                    save_best_only = True,\n",
    "                                    save_weights_only = True),\n",
    "    keras.callbacks.EarlyStopping(patience=5, min_delta=1e-3),\n",
    "]\n",
    "history = model.fit(x_train_scaled, y_train, epochs=10,\n",
    "                    validation_data=(x_valid_scaled, y_valid),\n",
    "                    callbacks = callbacks)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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5yEiIq7k3nDOov/8z//kscceA2DHjh0sWrSIs846iylTpgBQVlYGwNNPP80999yTel9paemg57788stT3ylubm7mqquu4s0338QYQzQaTZ33xhtvTA17d3/eJz7xCX73u99xzTXXsGLFCu6///5harGIiLiJe8N5CD3cI2HZsmU8/fTTrFixgnA4TE1NDXPnzuWNN94Y8jmMMan1SCSScSw/Pz+1/rWvfY1zzjmHxx57jK1btw76Hb5rrrmGD3zgAwSDQS6//HLNWYuI5CjNOffS3NxMaWkp4XCYN954gxdffJFIJMJzzz3H22+/DZAa1j7//PP5r//6r9R7u4e1q6qq2LBhA4lEItUDH+izxo8fD8C9996b2n/++efz61//mlgslvF548aNY9y4cdxxxx1cc801w9doERFxFYVzLxdeeCGxWIwTTjiB2267jVNPPZXKykoWLVrEhz70IebMmcMVV1wBwFe/+lX27dvHrFmzmDNnDkuXLgXge9/7HhdddBGnn346Y8eOHfCzvvjFL/KlL32JefPmpYIY4LrrrmPSpEmcdNJJzJkzh4ceeih17GMf+xgTJ07khBNOOEI/ARERyTaNi/YSCAT4y1/+0u+x973vfRnbBQUF/PrXv+5z68vLLruMyy67rM/703vHAKeddhqbNm1Kbd9xh3M7cp/Px49//GN+/OMf9znH8uXLuf7664fUFhERGZkUziPI/Pnzyc/P50c/+lG2qyIiIkeQwnkEWb16dbarICIiR4HmnEVERFxG4SwiIuIyCmcRERGXUTiLiIi4jMJZRETEZRTOhyH96VO9bd26lVmzZh3F2oiISK5QOIuIiLiMa7/n/P2Xvs8bjUN/2MRQzCibwb8v/PcBj992221MnDiRT3/60wDcfvvt+Hw+li5dSlNTE9FolDvuuINLLrnkoD43Eolw0003sWrVqtTdv8455xzWrVvHNddcQ1dXF4lEgkcffZRx48bxkY98hNraWuLxOF/72tdStwsVEZHRwbXhnA1XXHEF//qv/5oK58WLF/PUU09x6623UlRUxN69ezn11FO5+OKLM548NZi77roLYwxr167ljTfe4IILLmDTpk386le/4jOf+Qwf+9jH6OrqIh6P88QTTzBu3Dgef/xxwHk4hoiIjC6uDecD9XCPlHnz5rFnzx7eeecd6uvrKS0tpbq6ms9+9rM899xzeDwe6urq2L17N9XV1UM+7/Lly7nlllsAmDFjBscccwybNm3itNNO4zvf+Q61tbV86EMfYtq0acyePZt/+7d/49///d+56KKLOPPMM49Uc0VExKU059zL5ZdfziOPPMIf/vAHrrjiCh588EHq6+tZvXo1a9asoaqqqs8zmg/VP//zP7NkyRJCoRDvf//7eeaZZzj++ON5+eWXmT17Nl/96lf51re+NSyfJSIiI4dre87ZcsUVV3D99dezd+9enn32WRYvXsyYMWPw+/0sXbqUbdu2HfQ5zzzzTB588EHOPfdcNm3axPbt25k+fTpbtmxh6tSp3HrrrWzfvp3XXnuNGTNmUFZWxsc//nFKSkq45557jkArRUTEzRTOvcycOZPW1lbGjx/P2LFj+djHPsYHPvABZs+ezYIFC5gxY8ZBn/NTn/oUN910E7Nnz8bn83HvvfcSCARYvHgxDzzwAH6/n+rqar785S+zcuVKvvCFL+DxePD7/dx9991HoJUiIuJmCud+rF27NrVeUVHBihUr+i3X1tZGa2trv8cmT57M66+/DkAwGOS3v/1tnzK33XYbt912W8a+9773vbz3ve891KqLiEgO0JyziIiIy6jnfJjWrVvHjTfemLEvEAjwj3/8I0s1EhGRkU7hfJhmzpzJmjVrsl0NERHJIRrWFhERcRmFs4iIiMsonEVERFxG4SwiIuIyCufDcKDnOYuIiBwqhXMOiMVi2a6CiIgMI9d+lWrXd79L54bhfZ5z4IQZVH/5ywMeH87nObe1tXHJJZf0+77777+fH/7whxhjOOmkk3jggQfYvXs3N954I1u2bAHg7rvvZty4cVx00UWpO4398Ic/pK2tjdtvv52amhrmzp3L8uXLufLKKzn++OO544476Orqory8nAcffJCqqira2tq45ZZbWLVqFcYYvvGNb9Dc3Mxrr73GT3/6UwDuvfdetmzZwk9+8pPD+vmKiMjwcG04Z8NwPs85GAzy2GOP9Xnf+vXrueOOO3jhhReoqKigsbERgFtvvZWzzz6bxx57jHg8TltbG01NTQf8jK6uLlatWgVAU1MTL774IsYY7rnnHn7wgx/wox/9iG9/+9sUFxenbkna1NSE3+/nO9/5DnfeeSd+v5/f/e53esCGiIiLuDacD9TDPVKG83nO1lq+/OUv93nfM888w+WXX05F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\n",
      "text/plain": [
       "<Figure size 576x360 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "def plot_learning_curves(history):\n",
    "    pd.DataFrame(history.history).plot(figsize=(8, 5))\n",
    "    plt.grid(True)\n",
    "    plt.gca().set_ylim(0, 1)\n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(history)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10000/10000 [==============================] - 1s 63us/sample - loss: 0.3319 - accuracy: 0.8847s - loss: 0.3302 - \n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[0.33190771676301956, 0.8847]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.evaluate(x_test_scaled, y_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "No model found in config file.",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-8-c68dc195968e>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mloaded_model\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mkeras\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodels\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload_model\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput_model_file\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      2\u001b[0m \u001b[0mloaded_model\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mevaluate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx_test_scaled\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_test\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/workspace/environments/tf2_py3/lib/python3.7/site-packages/tensorflow/python/keras/saving/save.py\u001b[0m in \u001b[0;36mload_model\u001b[0;34m(filepath, custom_objects, compile)\u001b[0m\n\u001b[1;32m    135\u001b[0m   if (h5py is not None and (\n\u001b[1;32m    136\u001b[0m       isinstance(filepath, h5py.File) or h5py.is_hdf5(filepath))):\n\u001b[0;32m--> 137\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mhdf5_format\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload_model_from_hdf5\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcustom_objects\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcompile\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    138\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    139\u001b[0m   \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msix\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstring_types\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/workspace/environments/tf2_py3/lib/python3.7/site-packages/tensorflow/python/keras/saving/hdf5_format.py\u001b[0m in \u001b[0;36mload_model_from_hdf5\u001b[0;34m(filepath, custom_objects, compile)\u001b[0m\n\u001b[1;32m    157\u001b[0m     \u001b[0mmodel_config\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mattrs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'model_config'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    158\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mmodel_config\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 159\u001b[0;31m       \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'No model found in config file.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    160\u001b[0m     \u001b[0mmodel_config\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloads\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel_config\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'utf-8'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    161\u001b[0m     model = model_config_lib.model_from_config(model_config,\n",
      "\u001b[0;31mValueError\u001b[0m: No model found in config file."
     ]
    }
   ],
   "source": [
    "loaded_model = keras.models.load_model(output_model_file)\n",
    "loaded_model.evaluate(x_test_scaled, y_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.save_weights(os.path.join(logdir, \"fashion_mnist_weights_2.h5\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
